Skip to main content

Code management and Git access on the server

How the Ubuntu server gets application source code — and what to use instead once Docker + CI is in place.

All commands below are run on the Ubuntu server over SSH (as your normal login user, not apisvc).

Recommendation (summary)

ApproachUse whenVerdict
Your personal SSH key on the serverNever for GitAvoid — tied to you, often over-privileged
Read-only deploy key per repoManual git clone / git pull on serverBest for now
Dedicated deploy user + deploy keysSame, but cleaner separationGood if multiple people deploy
CI builds image → server docker pullDocker production targetBest long term — no Git on server

Do not put Git credentials on apisvc. That account runs the API only (nologin, no shell login).

Do not reuse your laptop’s SSH private key on the server for GitHub.


Three accounts — keep roles separate

your-ssh-user     SSH login from workstation; runs sudo and git pull
apisvc Runs gunicorn/Docker only; no Git, no login shell
(deploy) Optional: only Git + deploy scripts; no personal identity

Your existing SSH key is for logging into the server, not for GitHub.


Copy-paste: first repo (dwd-api-fastapi)

Run these blocks in order. Replace YOUR_USER only if $USER is empty.

0. Prerequisites (one-time)

# apisvc must exist (see docs/01-directory-layout.md)
id apisvc

# Allow your login user (e.g. gqc) to write under /opt/apis — re-login after usermod
sudo usermod -aG apisvc gqc

sudo mkdir -p /opt/apis
sudo chown root:apisvc /opt/apis
sudo chmod 775 /opt/apis

If you just ran usermod, log out and SSH back in before cloning. Verify with groups (must include apisvc) and touch /opt/apis/.write-test.

1. Generate deploy key

API=dwd-api-fastapi
GITHUB_ORG=gqc
GITHUB_REPO=dwd-api-fastapi

mkdir -p ~/.ssh
chmod 700 ~/.ssh

ssh-keygen -t ed25519 \
-f ~/.ssh/${API}_deploy \
-C "msi-laptop-3-${API}" \
-N ""

chmod 600 ~/.ssh/${API}_deploy
chmod 644 ~/.ssh/${API}_deploy.pub

echo "=== Add this public key to GitHub (next step) ==="
cat ~/.ssh/${API}_deploy.pub

2. Add key on GitHub

  1. Open: https://github.com/gqc/dwd-api-fastapi/settings/keys
  2. Add deploy key
  3. Title: MSI Laptop 3 — dwd-api-fastapi
  4. Key: paste output from cat ~/.ssh/${API}_deploy.pub
  5. Allow write access: leave unchecked (read-only)

3. SSH config and GitHub host key

API=dwd-api-fastapi
GITHUB_ORG=gqc
GITHUB_REPO=dwd-api-fastapi

ssh-keyscan -t ed25519 github.com >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts

cat >> ~/.ssh/config << EOF

Host github.com-${API}
HostName github.com
User git
IdentityFile ~/.ssh/${API}_deploy
IdentitiesOnly yes
EOF

chmod 600 ~/.ssh/config

4. Test GitHub access

API=dwd-api-fastapi

ssh -T git@github.com-${API}

Expected message (exact wording may vary):

Hi gqc/dwd-api-fastapi! You've successfully authenticated, but GitHub does not provide shell access.

5. Clone into /opt/apis/

Git runs as your login user (so your deploy key is used). Ownership is then set for apisvc.

API=dwd-api-fastapi
GITHUB_ORG=gqc
GITHUB_REPO=dwd-api-fastapi

git clone git@github.com-${API}:${GITHUB_ORG}/${GITHUB_REPO}.git \
/opt/apis/${API}

sudo chown -R apisvc:apisvc /opt/apis/${API}
sudo chmod -R g+rwX /opt/apis/${API}

Verify:

ls -la /opt/apis/dwd-api-fastapi

6. Routine deploy (manual)

API=dwd-api-fastapi

cd /opt/apis/${API}
git pull

# Native:
# sudo -u apisvc /opt/venvs/${API}/bin/pip install -r requirements.txt
# sudo systemctl restart ${API}

# Docker (from /etc/${API}/ or repo compose file):
# docker compose build && docker compose up -d

Copy-paste: additional API repo

Replace placeholders, then run the same blocks as above.

API=report-api                    # kebab-case api-name
GITHUB_ORG=gqc # GitHub org or user
GITHUB_REPO=report-api # repository name

Generate key, add deploy key on that repo’s GitHub settings, append a new Host block:

API=report-api

ssh-keygen -t ed25519 \
-f ~/.ssh/${API}_deploy \
-C "msi-laptop-3-${API}" \
-N ""

chmod 600 ~/.ssh/${API}_deploy
chmod 644 ~/.ssh/${API}_deploy.pub

cat ~/.ssh/${API}_deploy.pub

cat >> ~/.ssh/config << EOF

Host github.com-${API}
HostName github.com
User git
IdentityFile ~/.ssh/${API}_deploy
IdentitiesOnly yes
EOF

chmod 600 ~/.ssh/config

ssh -T git@github.com-${API}

GITHUB_ORG=gqc
GITHUB_REPO=report-api

git clone git@github.com-${API}:${GITHUB_ORG}/${GITHUB_REPO}.git \
/opt/apis/${API}

sudo chown -R apisvc:apisvc /opt/apis/${API}
sudo chmod -R g+rwX /opt/apis/${API}

GitHub allows one deploy key per repository — each API needs its own key pair and Host github.com-<api-name> entry.


Why not sudo -u apisvc git clone?

Deploy keys live in your ~/.ssh/. The apisvc user has no login shell and should not hold Git credentials.

Pattern used here:

  1. You run git clone / git pull (uses your deploy key).
  2. apisvc owns the files and runs the application.

Option B — Dedicated deploy user (optional)

Same commands as above, but run after switching to the deploy user (keys live in /home/deploy/.ssh/).

sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy
sudo usermod -aG apisvc deploy

sudo mkdir -p /opt/apis
sudo chown root:apisvc /opt/apis
sudo chmod 775 /opt/apis

Generate keys and clone as deploy:

sudo su - deploy

mkdir -p ~/.ssh
chmod 700 ~/.ssh

API=dwd-api-fastapi
ssh-keygen -t ed25519 \
-f ~/.ssh/${API}_deploy \
-C "msi-laptop-3-${API}" \
-N ""

chmod 600 ~/.ssh/${API}_deploy
chmod 644 ~/.ssh/${API}_deploy.pub

cat ~/.ssh/${API}_deploy.pub
# Add to GitHub deploy keys, then continue with ssh-keyscan / config / clone as deploy user

Your admin account SSHs in; run sudo -u deploy git -C /opt/apis/dwd-api-fastapi pull for deploys.


Option C — No Git on the server (Docker + CI target)

Once GitHub Actions (or Azure DevOps) builds and pushes images:

Developer → push to GitHub → CI builds image → GHCR/ACR
Server → docker pull (registry credentials only)

No deploy keys on the server. See 05-docker-strategy.md.


Troubleshooting

ProblemFix
Permission denied (publickey)Confirm deploy key on GitHub; IdentityFile path; IdentitiesOnly yes
Host key verification failedRun ssh-keyscan -t ed25519 github.com >> ~/.ssh/known_hosts
could not lock config filesudo chown $USER:$USER ~/.ssh/config && chmod 600 ~/.ssh/config
Permission denied writing /opt/apisAdd user to apisvc: sudo usermod -aG apisvc gqc, re-login; /opt/apis must be 775 not 755
git pull cannot write filessudo chown -R apisvc:apisvc /opt/apis/<api> and sudo chmod -R g+rwX /opt/apis/<api>; confirm you are in group apisvc (groups)
Wrong repo authenticatedEach repo needs its own Host github.com-<api-name> alias — do not reuse one Host for multiple repos

Test which key GitHub sees:

ssh -vT git@github.com-dwd-api-fastapi 2>&1 | grep "Offering public key"

What not to do

Anti-patternWhy
Copy your laptop’s private key to the serverSame key in two places; often full org access
One GitHub PAT on the server with broad repo scopeLarger blast radius than per-repo deploy keys
git clone as root without fixing ownershipApp should be owned by apisvc
Git credentials on apisvcRuntime account should not talk to GitHub
Deploy keys with write enabledServer only needs read

Checklist: first repo

  • id apisvc succeeds
  • sudo usermod -aG apisvc $USER and re-login
  • ~/.ssh/dwd-api-fastapi_deploy created (chmod 600)
  • Public key added to GitHub deploy keys (read-only)
  • Host github.com-dwd-api-fastapi in ~/.ssh/config
  • ssh -T git@github.com-dwd-api-fastapi succeeds
  • Cloned to /opt/apis/dwd-api-fastapi
  • sudo chown -R apisvc:apisvc /opt/apis/dwd-api-fastapi

When CI/CD is ready, remove deploy keys from the server and use image: from a registry in compose instead of build:.